/*
 * @Author: 0x9DEFA478
 * @Date: 2022-02-25 11:03:25
 * @LastEditTime: 2022-03-28 00:09:22
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * QQ:2652450237
 * ============================================================================================================================================
 * 
 * 
 * 
 *                                                                                               ************      ****************************
 *                                                                                             ************      ****************************  
 *                                                                                           ************      ****************************    
 *                                                                                         ************      ****************************      
 *                                                                                       ************      ************                        
 *                                                                                     ************      ************                          
 *                                                                                   ************      ************                            
 *                                                                                 ************      ************                              
 *                                                                               ************      ************                                
 *                                                                             ************      ************                                  
 *                                                                           ************      ************                                    
 *                                                                         ************      ************                                      
 *                                                                       ************      ************                                        
 *                                                                     ************      ************                                          
 *                                                                   ************      ************                                            
 *                                                                 ************      ************                                              
 *                                                               ************      ************                                                
 *                                                             ************      ************                                                  
 *                                                           ************      ************                                                    
 *                                                         ************      ************                                                      
 *                                                       ************      ************                                                        
 *                                                     ************      ************                                                          
 *                                                   ************      ************                                                            
 *                                                 ************      ************                                                              
 *                                               ************      ************                                                                
 *                                             ************      ************                                                                  
 *                                           ************      ************                                                                    
 *                                         ************      ************                                                                      
 *                                       ************      ************                                                                        
 *                                     ************      ************                                                                          
 *                                   ************      ************                                                                            
 *                                 ************      ************                                                                              
 *                               ************      ************                                                                                
 *                             ************      ************                                                                                  
 *                           ************      ************                                                                                    
 *                         ************      ************                                                                                      
 *       ****************************      ************                                                                                        
 *     ****************************      ************                                                                                          
 *   ****************************      ************                                                                                            
 * ****************************      ************                                                                                              
 * 
 * 
 * 
 * ============================================================================================================================================
 * 
 */
#include "../../Console/H_NES_Console.h"
#include "../../ConstString/H_NES_ConstString.h"




extern void H_NES_Mapper_AccessException(H_NES_Console* Console,int RetVal,const char* Format,Huint16 ErrorAddress);
extern void H_NES_Mapper_Warning(H_NES_Console* Console,int RetVal,const char* Format,Huint16 ErrorAddress);

extern void H_NES_Mapper_DefaultGetProgress(void* v,void* Progress);
extern void H_NES_Mapper_DefaultLoadProgress(void* v,void* Progress);
extern int H_NES_Mapper_DefaultProgressSize(void* v);




#define ThisMapper Cartridge.Mapper.SpecificMapper.Mapper1


static void MapperUpdate(void* v){
  H_NES_Console* Console;
  Hbyte* Base;
  Hbyte* p;
  Hbyte Reg_8000;
  Huint32 PRG_ROM_Size;
  Huint32 CHR_ROM_Size;
  Hint32 PRG_RAM_Size;
  Hint32 CHR_RAM_Size;

  Console=v;

  Reg_8000=Console->ThisMapper.Reg_8000;

  switch(Reg_8000&0x0003U){
    case 0x0U:
      Console->PPU.NameTable[0]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[1]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[2]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[3]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.VRAM_ScreenType=0x00U; 
      break;
    case 0x1U:
      Console->PPU.NameTable[0]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.NameTable[1]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.NameTable[2]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.NameTable[3]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.VRAM_ScreenType=0x00U;
      break;
    case 0x2U:
      //命名表水平排列
      Console->PPU.NameTable[0]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[1]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.NameTable[2]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[3]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.VRAM_ScreenType=0x02U;
      break;
    case 0x3U:
      //命名表垂直排列
      Console->PPU.NameTable[0]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[1]=&((Hbyte*)Console->PPU.VRAM)[0];
      Console->PPU.NameTable[2]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.NameTable[3]=&((Hbyte*)Console->PPU.VRAM)[0x400];
      Console->PPU.VRAM_ScreenType=0x01U;
      break;
    default:
      break;
  }

  

  PRG_ROM_Size=Console->Cartridge.Mapper.PRG_ROM_Size;
  Base=Console->Cartridge.Mapper.PRG_ROM;
  if(PRG_ROM_Size>0x40000U){
    PRG_ROM_Size-=0x40000U;//便于后续取余运算
    if((Console->ThisMapper.LastWrite&0x10U)!=0x00U){
      Base=&Base[0x40000U];
    }
  }
  switch((Reg_8000>>2)&0x0003U){
    case 0x0U:
    case 0x1U:
      p=&Base[(((Huint32)(Console->ThisMapper.Reg_E000&0x0EU))<<14)%PRG_ROM_Size];
      Console->ThisMapper.PRG_Banks.Base8000_16KB[0]=&p[0];
      Console->ThisMapper.PRG_Banks.Base8000_16KB[1]=&p[0x4000];
      break;
    case 0x2U:
      p=&Base[(((Huint32)(Console->ThisMapper.Reg_E000&0x0FU))<<14)%PRG_ROM_Size];
      Console->ThisMapper.PRG_Banks.Base8000_16KB[0]=Base;
      Console->ThisMapper.PRG_Banks.Base8000_16KB[1]=&p[0];
      break;
    case 0x3U:
      p=&Base[(((Huint32)(Console->ThisMapper.Reg_E000&0x0FU))<<14)%PRG_ROM_Size];
      Console->ThisMapper.PRG_Banks.Base8000_16KB[0]=&p[0];
      Console->ThisMapper.PRG_Banks.Base8000_16KB[1]=&Console->Cartridge.Mapper.PRG_ROM[Console->Cartridge.Mapper.PRG_ROM_Size-0x4000U];
      break;
    default:
      break;
  }

  CHR_RAM_Size=Console->Cartridge.Mapper.CHR_RAM_Size;
  Base=Console->Cartridge.Mapper.CHR_ROM;
  if((Reg_8000&0x10U)==0x00U){
    if(CHR_RAM_Size==0){
      CHR_ROM_Size=Console->Cartridge.Mapper.CHR_ROM_Size;

      p=&Base[(((Huint32)(Console->ThisMapper.Reg_A000&0x1EU))<<12)%CHR_ROM_Size];

      Console->ThisMapper.CHR_Banks.Base0000_4KB[0]=&p[0];
      Console->ThisMapper.CHR_Banks.Base0000_4KB[1]=&p[0x1000];
    }else{
      p=Base;

      Console->ThisMapper.CHR_Banks.Base0000_4KB[0]=&p[0];
      Console->ThisMapper.CHR_Banks.Base0000_4KB[1]=&p[0x1000];
    }
  }else{
    if(CHR_RAM_Size==0){
      CHR_ROM_Size=Console->Cartridge.Mapper.CHR_ROM_Size;

      Console->ThisMapper.CHR_Banks.Base0000_4KB[0]=&Base[(((Huint32)Console->ThisMapper.Reg_A000)<<12)%CHR_ROM_Size];
      Console->ThisMapper.CHR_Banks.Base0000_4KB[1]=&Base[(((Huint32)Console->ThisMapper.Reg_C000)<<12)%CHR_ROM_Size];
    
    }else{
      
      Console->ThisMapper.CHR_Banks.Base0000_4KB[0]=&Base[(((Huint32)(Console->ThisMapper.Reg_A000&0x01U))<<12)%CHR_RAM_Size];
      Console->ThisMapper.CHR_Banks.Base0000_4KB[1]=&Base[(((Huint32)(Console->ThisMapper.Reg_C000&0x01U))<<12)%CHR_RAM_Size];
    }
  }

  Base=Console->Cartridge.Mapper.PRG_RAM;
  if(Base==NULL){
    Console->ThisMapper.PRG_Banks.Base6000_8KB=NULL;
  }else{
    PRG_RAM_Size=Console->Cartridge.Mapper.PRG_RAM_Size;
    if(PRG_RAM_Size!=0){
      if(CHR_RAM_Size!=0){
        p=&Base[(((Huint32)((Console->ThisMapper.LastWrite>>2)&0x03U))<<13)%PRG_RAM_Size];
      }else{
        p=Base;
      }
      Console->ThisMapper.PRG_Banks.Base6000_8KB=p;
    }
  }

}




static void PRG_Write(Hbyte Value,Huint16 Address,void* v){
  H_NES_Console* Console;
  Hbyte* p;
  Hbyte NumOfBit;
  Hbyte t_byte;

  Console=v;

  if(Address<0x8000U){
    if(Address<0x6000U){
      H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_WriteFail,H_NES_ConstString_ExceptionPRGWriteAccess,Address);
      // for(;;){
      // }
      return;
    }

    if((Console->ThisMapper.Reg_E000&0x10U)!=0U){
      return;
    }

    p=Console->ThisMapper.PRG_Banks.Base6000_8KB;

    if(p==NULL){
      H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_WriteFail,H_NES_ConstString_ExceptionPRGRAMWriteAccess,Address);
      return;
      // for(;;){
      // }
    }

    p[Address-0x6000U]=Value;
  }else{
    if((Value&0x80U)!=0U){
      Console->ThisMapper.LoadRegister.NumOfBit=0x00U;
    }else{
      
      NumOfBit=Console->ThisMapper.LoadRegister.NumOfBit;
      t_byte=Console->ThisMapper.LoadRegister.Value;
      if(NumOfBit<0x04U){
        Console->ThisMapper.LoadRegister.NumOfBit=NumOfBit+0x01U;
        Console->ThisMapper.LoadRegister.Value=(t_byte>>1)|(Value<<7);
      }else{
        Console->ThisMapper.LoadRegister.NumOfBit=0x00U;
        t_byte=(((t_byte>>1)|(Value<<7))>>3)&0x1FU;

        switch(Address&0x6000U){
          case 0x0000U:
            Console->ThisMapper.Reg_8000=t_byte;
            break;
          case 0x2000U:
            Console->ThisMapper.Reg_A000=t_byte;
            break;
          case 0x4000U:
            Console->ThisMapper.Reg_C000=t_byte;
            Console->ThisMapper.LastWrite=t_byte;
            break;
          case 0x6000U:
            Console->ThisMapper.Reg_E000=t_byte;
            Console->ThisMapper.LastWrite=t_byte;
            break;
          default:
            break;
        }

        MapperUpdate(Console);
      }

    }
  }
}
static Hbyte PRG_Read(Huint16 Address,void* v){
  H_NES_Console* Console;
  Hbyte* p;

  Console=v;

  if(Address<0x8000U){
    if(Address<0x6000U){
      H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_ReadFail,H_NES_ConstString_ExceptionPRGReadAccess,Address);
      // for(;;){
      // }
      return Address>>8;
    }

    if((Console->ThisMapper.Reg_E000&0x10U)!=0U){
      return Address>>8;
    }

    p=Console->ThisMapper.PRG_Banks.Base6000_8KB;

    if(p==NULL){
      H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_ReadFail,H_NES_ConstString_ExceptionPRGRAMReadAccess,Address);
      // for(;;){
      // }
      
      return Address>>8;
    }

    return p[Address-0x6000U];
  }else{
    p=Console->ThisMapper.PRG_Banks.Base8000_16KB[(Address>>14)&0x0001U];
    
    return p[Address&0x3FFFU];
  }
}
static Huint16 PRG_Read16(Huint16 Address,void* v){
  H_NES_Console* Console;
  Hbyte* p;

  Console=v;

  if(Address<0x8000U){
    if(Address<0x6000U){
      H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_ReadFail,H_NES_ConstString_ExceptionPRGReadAccess,Address);
      // for(;;){
      // }
      return Address>>8;
    }

    if((Console->ThisMapper.Reg_E000&0x10U)!=0U){
      return Address>>8;
    }

    p=Console->ThisMapper.PRG_Banks.Base6000_8KB;

    if(p==NULL){
      H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_ReadFail,H_NES_ConstString_ExceptionPRGRAMReadAccess,Address);
      // for(;;){
      // }
      return Address>>8;
    }

    p=&p[Address-0x6000U];

    return (Huint16)p[0]|((Huint16)p[1]<<8);
  }else{
    p=Console->ThisMapper.PRG_Banks.Base8000_16KB[(Address>>14)&0x0001U];

    p=&p[Address&0x3FFFU];
    
    return (Huint16)p[0]|((Huint16)p[1]<<8);
  }
}




static void CHR_Exception_Write(Hbyte Value,Huint16 Address,void* v){
  H_NES_Console* Console;

  Console=v;
  
  H_NES_Mapper_Warning(Console,vH_NES_Result_PRG_WriteFail,H_NES_ConstString_ExceptionCHRWriteAccess,Address);
}
static void CHR_Write(Hbyte Value,Huint16 Address,void* v){
  H_NES_Console* Console;

  Console=v;

  Console->ThisMapper.CHR_Banks.Base0000_4KB[(Address>>12)&0x0001U][Address&0x0FFFU]=Value;
}
static Hbyte CHR_Read(Huint16 Address,void* v){
  H_NES_Console* Console;

  Console=v;

  return Console->ThisMapper.CHR_Banks.Base0000_4KB[(Address>>12)&0x0001U][Address&0x0FFFU];
}




static void DeInit(void* v){
  H_NES_Console* Console;

  Console=v;
  
  if(Console->Cartridge.Mapper.PRG_RAM!=NULL){
    Console->Memory.Free(Console->Cartridge.Mapper.PRG_RAM);
  }
}

static void GetProgress(void* v,void* Progress){
  H_NES_Console* Console;
  Hbyte* p;
  
  Console=v;
  p=Progress;

  H_NES_Mapper_DefaultGetProgress(Console,p);

  p=&p[H_NES_Mapper_DefaultProgressSize(Console)];

  p[0]=Console->ThisMapper.Reg_8000;
  p[1]=Console->ThisMapper.Reg_A000;
  p[2]=Console->ThisMapper.Reg_C000;
  p[3]=Console->ThisMapper.Reg_E000;

  p[4]=Console->ThisMapper.LoadRegister.Value;
  p[5]=Console->ThisMapper.LoadRegister.NumOfBit;

}

static void LoadProgress(void* v,void* Progress){
  H_NES_Console* Console;
  Hbyte* p;
  
  Console=v;
  p=Progress;

  H_NES_Mapper_DefaultLoadProgress(Console,p);

  p=&p[H_NES_Mapper_DefaultProgressSize(Console)];

  Console->ThisMapper.Reg_8000=p[0];
  Console->ThisMapper.Reg_A000=p[1];
  Console->ThisMapper.Reg_C000=p[2];
  Console->ThisMapper.Reg_E000=p[3];

  Console->ThisMapper.LoadRegister.Value=p[4];
  Console->ThisMapper.LoadRegister.NumOfBit=p[5];
  
  MapperUpdate(Console);
}

int H_NES_Mapper1_Init(void* v){
  H_NES_Console* Console;

  Console=v;


  if(Console->Cartridge.Mapper.PRG_RAM_Size<0){
    if(Console->Cartridge.Mapper.CHR_RAM_Size!=0U){
      Console->Cartridge.Mapper.PRG_RAM_Size=0x8000;
      Console->Cartridge.Mapper.PRG_RAM=Console->Memory.Malloc(0x8000);
    }else{
      Console->Cartridge.Mapper.PRG_RAM_Size=0x2000;
      Console->Cartridge.Mapper.PRG_RAM=Console->Memory.Malloc(0x2000);
    }
    if(Console->Cartridge.Mapper.PRG_RAM==NULL){
      return vH_NES_Result_PRG_RAM_AllocFail;
    }
  }else if(Console->Cartridge.Mapper.PRG_RAM_Size==0){
    Console->Cartridge.Mapper.PRG_RAM_Size=0x2000;
    Console->Cartridge.Mapper.PRG_RAM=Console->Memory.Malloc(0x2000);
    if(Console->Cartridge.Mapper.PRG_RAM==NULL){
      return vH_NES_Result_PRG_RAM_AllocFail;
    }
  }


  Console->ThisMapper.Reg_8000=0x0CU;
  Console->ThisMapper.Reg_A000=0x00U;
  Console->ThisMapper.Reg_C000=0x00U;
  Console->ThisMapper.Reg_E000=0x00U;

  Console->ThisMapper.LoadRegister.Value=0x00U;
  Console->ThisMapper.LoadRegister.NumOfBit=0x00U;

  MapperUpdate(Console);



  Console->Cartridge.Mapper.DeInit=DeInit;

  
  Console->Cartridge.PRG_Write=PRG_Write;
  Console->Cartridge.PRG_Read=PRG_Read;
  Console->Cartridge.PRG_Read16=PRG_Read16;
  
  if(Console->Cartridge.Mapper.CHR_ROM_Size==0U){
    Console->Cartridge.CHR_Write=CHR_Write;
  }else{
    Console->Cartridge.CHR_Write=CHR_Exception_Write;
  }
  Console->Cartridge.CHR_Read=CHR_Read;

  Console->Cartridge.Progress.Size=6+H_NES_Mapper_DefaultProgressSize(Console);
  Console->Cartridge.Progress.Get=GetProgress;
  Console->Cartridge.Progress.Load=LoadProgress;
  
  

  return vH_NES_Result_OK;
}




#undef ThisMapper



